package com.matthicks.macrobot;

import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.List;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class RemoteController {
	private CommandSender sender;
	
	public RemoteController(String host, int port) throws UnknownHostException, IOException {
		sender = new CommandSender(host, port);
	}
	
	public void changeDirectory(String directory) {
		sendCommand("changeDirectory", directory);
	}
	
	public void charType(char c) {
		sendCommand("charType", String.valueOf(c));
	}
	
	public void delay(float time) {
		sendCommand("delay", String.valueOf(time));
	}
	
	public void disconnect() throws IOException {
		sender.close();
	}
	
	public String getClipboardContents() {
		return (String)sendCommand("getClipboardContents");
	}
	
	public boolean isProcessRunning(String processName) {
		return (Boolean)sendCommand("isProcessRunning", processName);
	}
	
	public void keyPress(Key k) {
		sendCommand("keyPressByName", k.name());
	}
	
	public void keyRelease(Key k) {
		sendCommand("keyReleaseByName", k.name());
	}
	
	public void keyType(Key k) {
		sendCommand("keyTypeByName", k.name());
	}
	
	public void killProcess(String name) {
		sendCommand("killProcess", name);
	}
	
	public String[] listFilenames() {
		return (String[])sendCommand("listFilenames");
	}
	
	@SuppressWarnings("unchecked")
	public List<String> listRunningProcesses() {
		return (List<String>)sendCommand("listRunningProcesses");
	}
	
	public void mouseMove(int x, int y, float time) {
		sendCommand("mouseMove", String.valueOf(x), String.valueOf(y), String.valueOf(time));
	}
	
	public void mouseClick() {
		sendCommand("mouseClick");
	}
	
	public void mousePress() {
		sendCommand("mousePress");
	}
	
	public void mouseRelease() {
		sendCommand("mouseRelease");
	}
	
	public void saveContents(String content, String filename) {
		sendCommand("saveContents", content, filename);
	}
	
	public Point screenContains(int xOffset, int yOffset, String filename, float delay, int retries) {
		return (Point)sendCommand("screenContains", String.valueOf(xOffset), String.valueOf(yOffset), filename, String.valueOf(delay), String.valueOf(retries));
	}
	
	public Point findOnScreen(int xOffset, int yOffset, int xRange, int yRange, String filename) {
		return (Point)sendCommand("findOnScreen", String.valueOf(xOffset), String.valueOf(yOffset), String.valueOf(xRange), String.valueOf(yRange), filename);
	}
	
	public Point findOnScreen(int xOffset, int yOffset, int xRange, int yRange, String filename, float delay, int retries) {
		try {
			for (int i = 0; i < retries; i++) {
				long time = System.currentTimeMillis();
				Point p = (Point)sendCommand("findOnScreen", String.valueOf(xOffset), String.valueOf(yOffset), String.valueOf(xRange), String.valueOf(yRange), filename);
				if (p != null) {
					return p;
				}
				long wait = Math.round((delay * 1000) - (System.currentTimeMillis() - time));
				if (wait > 0) {
					Thread.sleep(wait);
				}
			}
			return null;
		} catch(InterruptedException exc) {
			throw new RuntimeException(exc);
		}
	}
	
	public void screenDisplay(int x, int y, int width, int height) {
		BufferedImage image = screenshot(x, y, width, height);
		ImageIcon icon = new ImageIcon(image);
		JFrame frame = new JFrame("Screenshot");
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		frame.setLayout(new FlowLayout());
		JLabel label = new JLabel(icon);
		frame.add(label);
		frame.pack();
		frame.setVisible(true);
	}
	
	public void screensave(int x, int y, int width, int height, String filename) {
		sendCommand("screenSave", String.valueOf(x), String.valueOf(y), String.valueOf(width), String.valueOf(height), filename);
	}
	
	public BufferedImage screenshot(int x, int y, int width, int height) {
		return (BufferedImage)sendCommand("screenshot", String.valueOf(x), String.valueOf(y), String.valueOf(width), String.valueOf(height));
	}
	
	public BufferedImage screenshot() {
		return (BufferedImage)sendCommand("screenshot");
	}
	
	public Object sendCommand(String name, String ... args) {
		Command c = new Command(name, args);
		CommandResponse r;
		try {
			r = sender.processCommand(c);
			if (r.successful) {
				return r.payload;
			} else {
				throw new RuntimeException("Command: " + name + " failed! " + r.payload);
			}
		} catch (IOException e) {
			throw new RuntimeException("Error processing command: " + name, e);
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("Error processing command: " + name, e);
		}
	}
	
	public void stringType(String s) {
		sendCommand("stringType", s);
	}
	
	public boolean waitForProcess(String processName, long timeout, boolean running) throws InterruptedException {
		long time = System.currentTimeMillis();
		while ((isProcessRunning(processName) != running) && (System.currentTimeMillis() - time < timeout)) {
			Thread.sleep(300);
		}
		return isProcessRunning(processName) == running;
	}
	
	public int containsImage(String ... images) {
		long time = System.currentTimeMillis();
		while (true) {
			for (int i = 0; i < images.length; i++) {
				try {
					Thread.sleep(500);
				} catch(InterruptedException exc) {
				}
				if (screenContains(0, 0, images[i], 0.0f, 1) != null) {
					return i;
				}
			}
			if (System.currentTimeMillis() - time > 180000) {
				return -1;
			}
		}
	}
}
